#pragma once

#include "alberto.destro.h"
#include "math_utils.h"
#include "../common/MathUtils.h"
#include <iostream>

bool AlbertoDestro::Init(Register& o_register)
{
	m_name = "ALBERTO DESTRO";

	REGISTER_FUNC(AlbertoDestro, 1);
	REGISTER_FUNC(AlbertoDestro, 2);
	REGISTER_FUNC(AlbertoDestro, 3);
	REGISTER_FUNC(AlbertoDestro, 4);
	REGISTER_FUNC(AlbertoDestro, 5);
	REGISTER_FUNC(AlbertoDestro, 6);
	REGISTER_FUNC(AlbertoDestro, 7);
	REGISTER_FUNC(AlbertoDestro, 9);
	REGISTER_FUNC(AlbertoDestro, 10);
	REGISTER_FUNC(AlbertoDestro, 12);

	return true;
}

RetType AlbertoDestro::Problem1()
{
	UInt32 sum = 0;
	UInt i = 0;
	while (i <1000)
	{
		sum += (i % 3 == 0 || i % 5 == 0) ? i : 0;
		i++;
	}
	return sum;
}

RetType AlbertoDestro::Problem2()
{
	//Find the sum of all the even-valued terms in the sequence which do not exceed four million.
	//starting with 1 and 2,

	UInt32 fibo_1 = 1;
	UInt32 fibo_2 = 2;
	
	UInt32 sum = 0;

	while (fibo_2 < 4000000)
	{
		UInt32 fibo_swap = fibo_1 + fibo_2;
		fibo_1 = fibo_2;
		fibo_2 = fibo_swap;
		if (fibo_1 % 2 == 0)
			sum += fibo_1;
	}

	return sum;
}


RetType AlbertoDestro::Problem3()
{
	//What is the largest prime factor of the number 600851475143 ?
	const UInt64 bound = 600851475143;
	UInt64 maxDiv = 1;

	for (UInt64 i = 3; i*i <= bound; i+=2)
	{
		if (((bound % i) == 0) && (math::IsPrime(static_cast<UInt>(i))))
			maxDiv = i;
	}

	return maxDiv;
}


RetType AlbertoDestro::Problem4()
{
	UInt32 maxPalin = 0;
	for (UInt32 i = 999; i > 99; --i )
	{
		for (UInt32 j = 999; j > 99; --j )
		{
			if (math::IsPalindrome(i*j) && i*j > maxPalin)
				maxPalin = i*j;
		}
	}

	return maxPalin; 
}


RetType AlbertoDestro::Problem5()
{
	//What is the smallest number that is evenly divisible by all of the numbers from 1 to 20?
	UInt result = 1;

	for (UInt i = 1; i <= 20 ; ++i)
	{
		result = math::GetMCM(result, i);
	}

	return static_cast<RetType>(result);
}

//Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum.
RetType AlbertoDestro::Problem6()
{
	UInt64 sum_of_squares = 0;
	UInt64 square_of_sum = 0;
	
	for (int i = 1; i <= 100; i++)
	{
		sum_of_squares += i*i;
		square_of_sum += i;
	}
	
	return (square_of_sum * square_of_sum - sum_of_squares);
}

RetType AlbertoDestro::Problem7()
{
	//What is the 10001^(st) prime number?
	const UInt32 maxPosition = 10001;

	UInt32 position = 1;
	UInt32 number = 1;

	while (position <= maxPosition -1)
	{
		number +=2;

		if (math::IsPrime(number))
			position++;
	}
	return number;
}

RetType AlbertoDestro::Problem10()
{
	/*Find the sum of all the primes below two million.*/
	UInt64 sum = 2;

	for (UInt64 i = 3; i < 2000000 ; i+=2)
	{
		if (math::IsPrime(static_cast<UInt>(i)))
			sum += i;
	}

	return sum;
}

//ugasoft: ok, we can assume there exists a triangle number with at least 500 divisors (in the range of a UInt64)
#pragma warning(push)
#pragma warning(disable: 4127) // C4127 conditional expression is constant

RetType AlbertoDestro::Problem12()
{
	/*What is the value of the first triangle number to have over five hundred divisors?*/

	UInt currentNum = 1;
	UInt updateTriangleElement = 2;

	while(true)
	{
		if (math::GetNumberOfDivisors(currentNum) >= 500)
			return currentNum;
		currentNum += updateTriangleElement++;
	}
}

#pragma warning(pop)

inline bool areCoprime(UInt16 num1, UInt16 num2)
{
	return (math::GetMCD(num1, num2) == 1);
}

RetType AlbertoDestro::Problem9()
{
	//There exists exactly one Pythagorean triplet for which a + b  + c = 1000.
	//Find the product abc.

	/*Ok, here come the math:
	a*a + b*b = c*c ... ok, patetic..
	but there's more: we can use this factorization:
	a = m*m - n*n 
	b = 2* m*n
	c = m*m + n*n
	(ok, thanks wiki)
	we can also multiply them (all of them) for a constant c... they still are pythagorean triplet !!
	NB: m and n are coprime and exactly one of them is even
	*/
	UInt16 maxRange = 50;

	for (UInt16 m = 2; m < maxRange; m++)
	{
		for (UInt16 n = 1; n < m; n++)
		{
			if (((m+n) & 1) && areCoprime(m,n))
			{	
				UInt16 c_mult = 1;
				UInt16 a = 0;
				UInt16 b = 0;
				UInt16 c = 0;
				while (!(a+b+c > 1000))
				{
					a = c_mult*(m*m - n*n);
					b = 2 * c_mult*(m*n);
					c = c_mult*(m*m + n*n);

					if (a + b + c == 1000)
						return (a*b*c);
					c_mult++;
				}
			}
		}
	}

	return 0;
}